//
//  main.cpp
//  WiFiMACSim
//
//  Created by Christopher Kemsley on 3/31/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include <iostream>
#include <string>
#include <sstream>

#include "CSMACAInitiator.hpp"
#include "CSMACAListener.hpp"
#include "CSMACAVCSInitiator.hpp"
#include "CSMACAVCSListener.hpp"
#include "CSMACAVCSInitiatorNoVCS.hpp"
#include "CSMACAVCSListenerNoVCS.hpp"
#include "Types.hpp"

#include "Settings.hpp"
#include "RunLoop.hpp"
#include "Medium.hpp"


using namespace Sim ;




void	constructSystem		( std::string const & simType , UInteger lambdaScalar ) ;
void	performLinking		( std::string const & configuration ) ;


std::set<Node*>	gAllNodes ;




int main(int argc, const char * argv[])
{
	
	if ( argc != 5 )
		{
		std::cout << "\n\nIncorrect argument count (was " << (argc-1) << ", expected 3" ;
		std::cout << "\n\nUsage: WiFiMACSim" ;
		std::cout << " <simType='CSMA/CA' or 'CSMA/CA VCS'>" ;		// Argument 1
		std::cout << " <lambda>" ;									// Argument 2
		std::cout << " <lambdaScalar=1 or 2>" ;						// Argument 3
		std::cout << " <configuration='standard' or 'hidden'>" ;	// Argument 4
		std::cout << "\n\n" ;
		return -1 ;
		}
	
	
	
	/* Interpret the arguments */
	
	std::string	simType			( argv[1] ) ;
	UInteger	lambda ;
	UInteger	lambdaScalar ;
	std::string	configuration	( argv[4] ) ;
	
	sscanf ( argv[2] , "%u" , &lambda ) ;
	sscanf ( argv[3] , "%u" , &lambdaScalar ) ;
	
	std::stringstream	str ;
	str << simType << "	" << lambda << "	" << lambdaScalar << "	" << configuration ;
	StateStatServer::rowPrefix = str.str() ;
	
	
	
	/** Verify the arguments **/
	
	if ( simType!="CSMA/CA" && simType!="CSMA/CA VCS" )
		{
		std::cout << "\n\n'" << simType << "' is not a valid simulation type" ;
		std::cout << "\n\nUsage: WiFiMACSim <simType='CSMA/CA' or 'CSMA/CA VCS'> <lambdaScalar=1 or 2> <configuration='standard' or 'hidden'>\n\n" ;
		return -1 ;
		}
	if ( configuration!="standard" && configuration!="hidden" )
		{
		std::cout << "\n\n'" << simType << "' is not a valid configuration" ;
		std::cout << "\n\nUsage: WiFiMACSim <simType='CSMA/CA' or 'CSMA/CA VCS'> <lambdaScalar=1 or 2> <configuration='standard' or 'hidden'>\n\n" ;
		return -1 ;
		}
	if ( lambdaScalar==0 || lambdaScalar>2 )
		{
		std::cout << "\n\n'" << lambdaScalar << "' is not a valid lambda scalar" ;
		std::cout << "\n\nUsage: WiFiMACSim <simType='CSMA/CA' or 'CSMA/CA VCS'> <lambdaScalar=1 or 2> <configuration='standard' or 'hidden'>\n\n" ;
		return -1 ;
		}
	if ( lambda==0 )
		{
		std::cout << "\n\nLambda cannot be zero" ;
		std::cout << "\n\nUsage: WiFiMACSim <simType='CSMA/CA' or 'CSMA/CA VCS'> <lambdaScalar=1 or 2> <configuration='standard' or 'hidden'>\n\n" ;
		return -1 ;
		}
	
	
	
	/** Set up the environment **/
	
	Settings::shared() . lambda	=	lambda ;
	
	constructSystem ( simType , lambdaScalar ) ;
	performLinking ( configuration ) ;
	
	// Make it short for debugging
	//Settings::shared() . simulationDuration = 500 ;
	
	
	
	/** Run the simulation **/
	
	try
		{
		RunLoop::currentRunLoop() . runAllModesRepeatedly ( Settings::shared().simulationDuration ) ;
		}
	catch ( Exception & e )
		{
		std::cout << "Encountered exception: " << e << "\n" ;
		}
	catch ( ... )
		{
		std::cout << "Encountered unknown exception\n" ;
		}
	
	
	
	/** Destroy the nodes **/
	std::set<Node*>::iterator	here	=	gAllNodes.begin() ;
	std::set<Node*>::iterator	end		=	gAllNodes.end() ;
	while ( here != end )
		{
		Node* n = *(here++) ;
		delete n ;
		}
	
	
	
	std::cout << std::flush ;
	
	
	
    return 0;
}





void	constructSystem		( std::string const & simType , UInteger lambdaScalar )
	{
	if ( simType == "CSMA/CA" )
		{
		/*Node * n ;
		n = new CSMACAInitiator ( 'A' , 'B' , lambdaScalar ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACAListener ( 'B' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		
		n = new CSMACAInitiator ( 'C' , 'D' , 1 ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACAListener ( 'D' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;*/
		
		Node * n ;
		n = new CSMACANoVCSInitiator ( 'A' , 'B' , lambdaScalar ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACANoVCSListener ( 'B' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		
		n = new CSMACANoVCSInitiator ( 'C' , 'D' , 1 ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACANoVCSListener ( 'D' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		}
	else
		{
		Node * n ;
		n = new CSMACAVCSInitiator ( 'A' , 'B' , lambdaScalar ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACAVCSListener ( 'B' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		
		n = new CSMACAVCSInitiator ( 'C' , 'D' , 1 ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		
		n = new CSMACAVCSListener ( 'D' ) ;
		RunLoop::currentRunLoop() . addSource ( n ) ;
		gAllNodes.insert(n) ;
		}
	}
void	performLinking		( std::string const & configuration )
	{
	Medium & m = Medium::shared() ;
	
	if ( configuration == "standard" )
		{
		m.buildConnectionPair	( 'A' , 'B' ) ;
		m.buildConnectionPair	( 'A' , 'C' ) ;
		m.buildConnectionPair	( 'A' , 'D' ) ;
		m.buildConnectionPair	( 'B' , 'C' ) ;
		m.buildConnectionPair	( 'B' , 'D' ) ;
		m.buildConnectionPair	( 'C' , 'D' ) ;
		}
	else
		{
		m.buildConnectionPair	( 'A' , 'B' ) ;
		m.buildConnectionPair	( 'B' , 'C' ) ;
		m.buildConnectionPair	( 'C' , 'D' ) ;
		}
	}

